-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[HLSL] Global resource arrays element access #152454
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[HLSL] Global resource arrays element access #152454
Conversation
…ing and make them static If a resource array does not have an explicit binding attribute, SemaHLSL will add an implicit one. The attribute will be used to transfer implicit binding order ID to the codegen, the same way as it is done for HLSLBufferDecls. This is necessary in order to generate correct initialization of resources in an array that does not have an explicit binding. This change also marks resource arrays declared at a global scope as `static`, which is what is already done for standalone resources.
… resource-array-impl-binding-attr
Adds support for accessing individual resources from fixed-size resource arrays declared at global scope. When a global resource array is indexed to retrieve a specific resource, the codegen translates the `ArraySubscriptExpr` into a constructor call for the corresponding resource record type and binding. Closes llvm#145424
@llvm/pr-subscribers-hlsl @llvm/pr-subscribers-clang-codegen Author: Helena Kotas (hekota) ChangesAdds support for accessing individual resources from fixed-size global resource arrays. Design proposal: https://github.com/llvm/wg-hlsl/blob/main/proposals/0028-resource-arrays.md Enables indexing into globally scoped, fixed-size resource arrays to retrieve individual resources. The initialization logic is primarily handled during codegen. When a global resource array is indexed, the codegen translates the To support this behavior, Sema needs to ensure that:
Closes #145424 Depends on #152450 and #152452. Patch is 28.60 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/152454.diff 9 Files Affected:
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 085c9ed9f3ebd..0c215c6e10013 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -229,10 +229,17 @@ class SemaHLSL : public SemaBase {
void diagnoseAvailabilityViolations(TranslationUnitDecl *TU);
- bool initGlobalResourceDecl(VarDecl *VD);
uint32_t getNextImplicitBindingOrderID() {
return ImplicitBindingNextOrderID++;
}
+
+ bool initGlobalResourceDecl(VarDecl *VD);
+ bool initGlobalResourceArrayDecl(VarDecl *VD);
+ void createResourceRecordCtorArgs(const Type *ResourceTy, StringRef VarName,
+ HLSLResourceBindingAttr *RBA,
+ HLSLVkBindingAttr *VkBinding,
+ uint32_t ArrayIndex,
+ llvm::SmallVector<Expr *> &Args);
};
} // namespace clang
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index ed35a055d8a7f..8c34fb501a3b8 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -16,6 +16,7 @@
#include "CGCall.h"
#include "CGCleanup.h"
#include "CGDebugInfo.h"
+#include "CGHLSLRuntime.h"
#include "CGObjCRuntime.h"
#include "CGOpenMPRuntime.h"
#include "CGRecordLayout.h"
@@ -4532,6 +4533,15 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
LHS.getBaseInfo(), TBAAAccessInfo());
}
+ // The HLSL runtime handle the subscript expression on global resource arrays.
+ if (getLangOpts().HLSL && (E->getType()->isHLSLResourceRecord() ||
+ E->getType()->isHLSLResourceRecordArray())) {
+ std::optional<LValue> LV =
+ CGM.getHLSLRuntime().emitResourceArraySubscriptExpr(E, *this);
+ if (LV.has_value())
+ return *LV;
+ }
+
// All the other cases basically behave like simple offsetting.
// Handle the extvector case we ignored above.
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 918cb3e38448d..a09e540367a18 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -84,6 +84,124 @@ void addRootSignature(llvm::dxbc::RootSignatureVersion RootSigVer,
RootSignatureValMD->addOperand(MDVals);
}
+// If the specified expr is a simple decay from an array to pointer,
+// return the array subexpression. Otherwise, return nullptr.
+static const Expr *getSubExprFromArrayDecayOperand(const Expr *E) {
+ const auto *CE = dyn_cast<CastExpr>(E);
+ if (!CE || CE->getCastKind() != CK_ArrayToPointerDecay)
+ return nullptr;
+ return CE->getSubExpr();
+}
+
+// Find array variable declaration from nested array subscript AST nodes
+static const ValueDecl *getArrayDecl(const ArraySubscriptExpr *ASE) {
+ const Expr *E = nullptr;
+ while (ASE != nullptr) {
+ E = getSubExprFromArrayDecayOperand(ASE->getBase());
+ if (!E)
+ return nullptr;
+ ASE = dyn_cast<ArraySubscriptExpr>(E);
+ }
+ if (const DeclRefExpr *DRE = dyn_cast_or_null<DeclRefExpr>(E))
+ return DRE->getDecl();
+ return nullptr;
+}
+
+// Get the total size of the array, or -1 if the array is unbounded.
+static int getTotalArraySize(const clang::Type *Ty) {
+ assert(Ty->isArrayType() && "expected array type");
+ if (Ty->isIncompleteArrayType())
+ return -1;
+ int Size = 1;
+ while (const auto *CAT = dyn_cast<ConstantArrayType>(Ty)) {
+ Size *= CAT->getSExtSize();
+ Ty = CAT->getArrayElementTypeNoTypeQual();
+ }
+ return Size;
+}
+
+// Find constructor decl for a specific resource record type and binding
+// (implicit vs. explicit). The constructor has 6 parameters.
+// For explicit binding the signature is:
+// void(unsigned, unsigned, int, unsigned, const char *).
+// For implicit binding the signature is:
+// void(unsigned, int, unsigned, unsigned, const char *).
+static CXXConstructorDecl *findResourceConstructorDecl(ASTContext &AST,
+ QualType ResTy,
+ bool ExplicitBinding) {
+ SmallVector<QualType> ExpParmTypes = {
+ AST.UnsignedIntTy, AST.UnsignedIntTy, AST.UnsignedIntTy,
+ AST.UnsignedIntTy, AST.getPointerType(AST.CharTy.withConst())};
+ ExpParmTypes[ExplicitBinding ? 2 : 1] = AST.IntTy;
+
+ CXXRecordDecl *ResDecl = ResTy->getAsCXXRecordDecl();
+ for (auto *Ctor : ResDecl->ctors()) {
+ if (Ctor->getNumParams() != ExpParmTypes.size())
+ continue;
+ ParmVarDecl **ParmIt = Ctor->param_begin();
+ QualType *ExpTyIt = ExpParmTypes.begin();
+ for (; ParmIt != Ctor->param_end() && ExpTyIt != ExpParmTypes.end();
+ ++ParmIt, ++ExpTyIt) {
+ if ((*ParmIt)->getType() != *ExpTyIt)
+ break;
+ }
+ if (ParmIt == Ctor->param_end())
+ return Ctor;
+ }
+ llvm_unreachable("did not find constructor for resource class");
+}
+
+static Value *buildNameForResource(llvm::StringRef BaseName,
+ CodeGenModule &CGM) {
+ std::string Str(BaseName);
+ std::string GlobalName(Str + ".str");
+ return CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer();
+}
+
+static void createResourceCtorArgs(CodeGenModule &CGM, CXXConstructorDecl *CD,
+ llvm::Value *ThisPtr, llvm::Value *Range,
+ llvm::Value *Index, StringRef Name,
+ HLSLResourceBindingAttr *RBA,
+ HLSLVkBindingAttr *VkBinding,
+ CallArgList &Args) {
+ assert((VkBinding || RBA) && "at least one a binding attribute expected");
+
+ std::optional<uint32_t> RegisterSlot;
+ uint32_t SpaceNo = 0;
+ if (VkBinding) {
+ RegisterSlot = VkBinding->getBinding();
+ SpaceNo = VkBinding->getSet();
+ } else if (RBA) {
+ if (RBA->hasRegisterSlot())
+ RegisterSlot = RBA->getSlotNumber();
+ SpaceNo = RBA->getSpaceNumber();
+ }
+
+ ASTContext &AST = CD->getASTContext();
+ Value *NameStr = buildNameForResource(Name, CGM);
+ Value *Space = llvm::ConstantInt::get(CGM.IntTy, SpaceNo);
+
+ Args.add(RValue::get(ThisPtr), CD->getThisType());
+ if (RegisterSlot.has_value()) {
+ // explicit binding
+ auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, RegisterSlot.value());
+ Args.add(RValue::get(RegSlot), AST.UnsignedIntTy);
+ Args.add(RValue::get(Space), AST.UnsignedIntTy);
+ Args.add(RValue::get(Range), AST.IntTy);
+ Args.add(RValue::get(Index), AST.UnsignedIntTy);
+
+ } else {
+ // implicit binding
+ auto *OrderID =
+ llvm::ConstantInt::get(CGM.IntTy, RBA->getImplicitBindingOrderID());
+ Args.add(RValue::get(Space), AST.UnsignedIntTy);
+ Args.add(RValue::get(Range), AST.IntTy);
+ Args.add(RValue::get(Index), AST.UnsignedIntTy);
+ Args.add(RValue::get(OrderID), AST.UnsignedIntTy);
+ }
+ Args.add(RValue::get(NameStr), AST.getPointerType(AST.CharTy.withConst()));
+}
+
} // namespace
llvm::Type *
@@ -590,13 +708,6 @@ static void initializeBuffer(CodeGenModule &CGM, llvm::GlobalVariable *GV,
CGM.AddCXXGlobalInit(InitResFunc);
}
-static Value *buildNameForResource(llvm::StringRef BaseName,
- CodeGenModule &CGM) {
- std::string Str(BaseName);
- std::string GlobalName(Str + ".str");
- return CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer();
-}
-
void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
llvm::GlobalVariable *GV,
HLSLVkBindingAttr *VkBinding) {
@@ -624,17 +735,13 @@ void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
auto *Index = llvm::ConstantInt::get(CGM.IntTy, 0);
auto *RangeSize = llvm::ConstantInt::get(CGM.IntTy, 1);
auto *Space = llvm::ConstantInt::get(CGM.IntTy, RBA->getSpaceNumber());
- Value *Name = nullptr;
+ Value *Name = buildNameForResource(BufDecl->getName(), CGM);
llvm::Intrinsic::ID IntrinsicID =
RBA->hasRegisterSlot()
? CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic()
: CGM.getHLSLRuntime().getCreateHandleFromImplicitBindingIntrinsic();
- std::string Str(BufDecl->getName());
- std::string GlobalName(Str + ".str");
- Name = CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer();
-
// buffer with explicit binding
if (RBA->hasRegisterSlot()) {
auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, RBA->getSlotNumber());
@@ -701,3 +808,95 @@ void CGHLSLRuntime::emitInitListOpaqueValues(CodeGenFunction &CGF,
}
}
}
+
+std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
+ const ArraySubscriptExpr *ArraySubsExpr, CodeGenFunction &CGF) {
+ assert(ArraySubsExpr->getType()->isHLSLResourceRecord() ||
+ ArraySubsExpr->getType()->isHLSLResourceRecordArray() &&
+ "expected resource array subscript expression");
+
+ // let clang codegen handle local resource array subscrips
+ const VarDecl *ArrayDecl = dyn_cast<VarDecl>(getArrayDecl(ArraySubsExpr));
+ if (!ArrayDecl || !ArrayDecl->hasGlobalStorage())
+ return std::nullopt;
+
+ // FIXME: this is not yet implemented (llvm/llvm-project#145426)
+ assert(!ArraySubsExpr->getType()->isArrayType() &&
+ "indexing of array subsets it not supported yet");
+
+ // get total array size (= range size)
+ const Type *ResArrayTy = ArrayDecl->getType().getTypePtr();
+ assert(ResArrayTy->isHLSLResourceRecordArray() &&
+ "expected array of resource classes");
+ llvm::Value *Range =
+ llvm::ConstantInt::get(CGM.IntTy, getTotalArraySize(ResArrayTy));
+
+ // Iterate through all nested array subscript expressions to calculate
+ // the index in the flattened resource array (if this is a multi-
+ // dimensional array). The index is calculated as a sum of all indices
+ // multiplied by the total size of the array at that level.
+ Value *Index = nullptr;
+ Value *Multiplier = nullptr;
+ const ArraySubscriptExpr *ASE = ArraySubsExpr;
+ while (ASE != nullptr) {
+ Value *SubIndex = CGF.EmitScalarExpr(ASE->getIdx());
+ if (const auto *ArrayTy =
+ dyn_cast<ConstantArrayType>(ASE->getType().getTypePtr())) {
+ Value *SubMultiplier =
+ llvm::ConstantInt::get(CGM.IntTy, ArrayTy->getSExtSize());
+ Multiplier = Multiplier ? CGF.Builder.CreateMul(Multiplier, SubMultiplier)
+ : SubMultiplier;
+ SubIndex = CGF.Builder.CreateMul(SubIndex, Multiplier);
+ }
+
+ Index = Index ? CGF.Builder.CreateAdd(Index, SubIndex) : SubIndex;
+ ASE = dyn_cast<ArraySubscriptExpr>(
+ getSubExprFromArrayDecayOperand(ASE->getBase()));
+ }
+
+ // find binding info for the resource array
+ // (for implicit binding an HLSLResourceBindingAttr should have been added by
+ // SemaHLSL)
+ QualType ResourceTy = ArraySubsExpr->getType();
+ HLSLVkBindingAttr *VkBinding = ArrayDecl->getAttr<HLSLVkBindingAttr>();
+ HLSLResourceBindingAttr *RBA = ArrayDecl->getAttr<HLSLResourceBindingAttr>();
+ assert((VkBinding || RBA) && "resource array must have a binding attribute");
+
+ // lookup the resource class constructor based on the resource type and
+ // binding
+ CXXConstructorDecl *CD =
+ findResourceConstructorDecl(ArrayDecl->getASTContext(), ResourceTy,
+ VkBinding || RBA->hasRegisterSlot());
+
+ // create a temporary variable for the resource class instance (we need to
+ // return an LValue)
+ RawAddress TmpVar = CGF.CreateMemTemp(ResourceTy);
+ if (auto *Size = CGF.EmitLifetimeStart(
+ CGM.getDataLayout().getTypeAllocSize(TmpVar.getElementType()),
+ TmpVar.getPointer())) {
+ CGF.pushFullExprCleanup<CodeGenFunction::CallLifetimeEnd>(
+ NormalEHLifetimeMarker, TmpVar, Size);
+ }
+ AggValueSlot ValueSlot = AggValueSlot::forAddr(
+ TmpVar, Qualifiers(), AggValueSlot::IsDestructed_t(true),
+ AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false),
+ AggValueSlot::MayOverlap);
+
+ Address ThisAddress = ValueSlot.getAddress();
+ llvm::Value *ThisPtr = CGF.getAsNaturalPointerTo(
+ ThisAddress, CD->getThisType()->getPointeeType());
+
+ // assemble the constructor parameters
+ CallArgList Args;
+ createResourceCtorArgs(CGM, CD, ThisPtr, Range, Index, ArrayDecl->getName(),
+ RBA, VkBinding, Args);
+
+ // call the constructor
+ CGF.EmitCXXConstructorCall(CD, Ctor_Complete, false, false, ThisAddress, Args,
+ ValueSlot.mayOverlap(),
+ ArraySubsExpr->getExprLoc(),
+ ValueSlot.isSanitizerChecked());
+
+ return CGF.MakeAddrLValue(TmpVar, ArraySubsExpr->getType(),
+ AlignmentSource::Decl);
+}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index 31d1728da9c56..b872f9ef0e9b6 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -68,6 +68,7 @@ class Type;
class RecordType;
class DeclContext;
class HLSLPackOffsetAttr;
+class ArraySubscriptExpr;
class FunctionDecl;
@@ -75,6 +76,7 @@ namespace CodeGen {
class CodeGenModule;
class CodeGenFunction;
+class LValue;
class CGHLSLRuntime {
public:
@@ -164,6 +166,10 @@ class CGHLSLRuntime {
llvm::TargetExtType *LayoutTy);
void emitInitListOpaqueValues(CodeGenFunction &CGF, InitListExpr *E);
+ std::optional<LValue>
+ emitResourceArraySubscriptExpr(const ArraySubscriptExpr *E,
+ CodeGenFunction &CGF);
+
private:
void emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
llvm::GlobalVariable *BufGV);
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 834b1c067d84c..1498c5d75fa53 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5775,8 +5775,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
if (D->getType()->isReferenceType())
T = D->getType();
- if (getLangOpts().HLSL &&
- D->getType().getTypePtr()->isHLSLResourceRecord()) {
+ if (getLangOpts().HLSL && (D->getType()->isHLSLResourceRecord() ||
+ D->getType()->isHLSLResourceRecordArray())) {
Init = llvm::PoisonValue::get(getTypes().ConvertType(ASTTy));
NeedsGlobalCtor = true;
} else if (getLangOpts().CPlusPlus) {
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 6811f3f27603b..7de529fc898ad 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -357,6 +357,14 @@ getResourceArrayHandleType(VarDecl *VD) {
return HLSLAttributedResourceType::findHandleTypeOnResource(Ty);
}
+// returns the element type of an array (including multi-dimensional array)
+static QualType getArrayElementType(QualType Ty) {
+ assert(Ty->isArrayType() && "expected array type");
+ while (const ArrayType *AT = dyn_cast<ArrayType>(Ty.getTypePtr()))
+ Ty = AT->getElementType();
+ return Ty;
+}
+
// Returns true if the type is a leaf element type that is not valid to be
// included in HLSL Buffer, such as a resource class, empty struct, zero-sized
// array, or a builtin intangible type. Returns false it is a valid leaf element
@@ -3698,11 +3706,14 @@ static bool initVarDeclWithCtor(Sema &S, VarDecl *VD,
return true;
}
-bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
+void SemaHLSL::createResourceRecordCtorArgs(const Type *ResourceTy,
+ StringRef VarName,
+ HLSLResourceBindingAttr *RBA,
+ HLSLVkBindingAttr *VkBinding,
+ uint32_t ArrayIndex,
+ llvm::SmallVector<Expr *> &Args) {
std::optional<uint32_t> RegisterSlot;
uint32_t SpaceNo = 0;
- HLSLVkBindingAttr *VkBinding = VD->getAttr<HLSLVkBindingAttr>();
- HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
if (VkBinding) {
RegisterSlot = VkBinding->getBinding();
SpaceNo = VkBinding->getSet();
@@ -3717,12 +3728,12 @@ bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
uint64_t IntTySize = AST.getTypeSize(AST.IntTy);
IntegerLiteral *RangeSize = IntegerLiteral::Create(
AST, llvm::APInt(IntTySize, 1), AST.IntTy, SourceLocation());
- IntegerLiteral *Index = IntegerLiteral::Create(
- AST, llvm::APInt(UIntTySize, 0), AST.UnsignedIntTy, SourceLocation());
+ IntegerLiteral *Index =
+ IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, ArrayIndex),
+ AST.UnsignedIntTy, SourceLocation());
IntegerLiteral *Space =
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, SpaceNo),
AST.UnsignedIntTy, SourceLocation());
- StringRef VarName = VD->getName();
StringLiteral *Name = StringLiteral::Create(
AST, VarName, StringLiteralKind::Ordinary, false,
AST.getStringLiteralArrayType(AST.CharTy.withConst(), VarName.size()),
@@ -3733,18 +3744,57 @@ bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
IntegerLiteral *RegSlot = IntegerLiteral::Create(
AST, llvm::APInt(UIntTySize, RegisterSlot.value()), AST.UnsignedIntTy,
SourceLocation());
- Expr *Args[] = {RegSlot, Space, RangeSize, Index, Name};
- return initVarDeclWithCtor(SemaRef, VD, Args);
+ Args.append({RegSlot, Space, RangeSize, Index, Name});
+ } else {
+ // resource with implicit binding
+ uint32_t OrderID = (RBA && RBA->hasImplicitBindingOrderID())
+ ? RBA->getImplicitBindingOrderID()
+ : getNextImplicitBindingOrderID();
+ IntegerLiteral *OrderId =
+ IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, OrderID),
+ AST.UnsignedIntTy, SourceLocation());
+ Args.append({Space, RangeSize, Index, OrderId, Name});
}
+}
- // resource with implicit binding
- IntegerLiteral *OrderId = IntegerLiteral::Create(
- AST, llvm::APInt(UIntTySize, getNextImplicitBindingOrderID()),
- AST.UnsignedIntTy, SourceLocation());
- Expr *Args[] = {Space, RangeSize, Index, OrderId, Name};
+bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
+ SmallVector<Expr *> Args;
+ createResourceRecordCtorArgs(VD->getType().getTypePtr(), VD->getName(),
+ VD->getAttr<HLSLResourceBindingAttr>(),
+ VD->getAttr<HLSLVkBindingAttr>(), 0, Args);
return initVarDeclWithCtor(SemaRef, VD, Args);
}
+bool SemaHLSL::initGlobalResourceArrayDecl(VarDecl *VD) {
+ assert(VD->getType()->isHLSLResourceRecordArray() &&
+ "expected array of resource records");
+
+ // Individual resources in a resource array are not initialized here. They
+ // are initialized later on during codegen when the individual resources are
+ // accessed. Codegen will emit a call to the resource constructor with the
+ // specified array index. We need to make sure though that the constructor
+ // for the specific resource type is instantiated, so codegen can emit a call
+ // to it when the array element is accessed.
+ SmallVector<Expr *> Args;
+ QualType ResElementTy = getArrayElementType(VD->getType());
+ createResourceRecordCtorArgs(ResElementTy.getTypePtr(), VD->getName(),
+ VD->getAttr<HLSLResourceBindingAttr>(),
+ VD->getAttr<HLSLVkBindingAttr>(), 0, Args);
+
+ SourceLocation Loc = VD->getLocation();
+ InitializedEntity Entity =
+ InitializedEntity::InitializeTemporary(ResElementTy);
+ InitializationKind Kind = InitializationKind::CreateDirect(Loc, Loc, Loc);
+ InitializationSequence InitSeq(SemaRef, Entity, Kind, Args);
+ if (InitSeq.Failed())
+ return false;
+
+ // This takes care of instantiating and emitting of the constructor that will
+ // be called from codegen when the array is accessed.
+ ExprResult OneResInit = InitSeq.Perform(SemaRef, Entity, Kind, Args);
+ return !OneResInit.isInvalid();
+}
+
// Returns true if the initialization has been handled.
...
[truncated]
|
// Make sure A[2] is translated to a RWBuffer<float> constructor call with range 4 and index 2 | ||
// and DXIL explicit binding (u10, space1) | ||
// and SPIR-V explicit binding (binding 12, set 2) | ||
// DXIL: call void @_ZN4hlsl8RWBufferIfEC1EjjijPKc(ptr {{.*}} %[[Tmp0]], i32 noundef 10, i32 noundef 1, i32 noundef 4, i32 noundef 2, ptr noundef @[[BufA]]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious about the mangled name in here '@_ZN4hlsl8RWBufferIfEC1EjjijPKc' Could that change? More so I'm wondering why we have that as part of the string we're matching here. To me it seems like that would be something we don't car as much about.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mangled name should not change as long as the constructor signature stays the same. We do want to verify that we are calling the correct constructor overload. In this case it is hlsl::RWBuffer
with float
type template parameter and arguments (uint, uint, int, uint, const char*) == jjijPKc
, which is the signature for resource constructor with explicit binding. See Itanium ABI mangling.
The C1
is a type of constructor and it has to do with virtual overloading (https://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable-ctor).
clang/test/CodeGenHLSL/resources/res-array-global-multi-dim.hlsl
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Submitting a couple comments.
Value *Multiplier = nullptr; | ||
const ArraySubscriptExpr *ASE = ArraySubsExpr; | ||
while (ASE != nullptr) { | ||
Value *SubIndex = CGF.EmitScalarExpr(ASE->getIdx()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could SubIndex ever be null?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so. The result of EmitScalarExpr
is generally assumed to be non-null.
@@ -701,3 +808,95 @@ void CGHLSLRuntime::emitInitListOpaqueValues(CodeGenFunction &CGF, | |||
} | |||
} | |||
} | |||
|
|||
std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this function be better named? I don't think it emits a resource array subscript expr?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the name is quite descriptive - it is emitting code for subscript expression on resource arrays, or in other words, emitting code for ArraySubscriptExpr
AST node on a resources array. I think it matches the existing patterns of CGF.Emit*
, such as CGF.EmitScalarExp
or CGF.EmitCallExpr
. Do you have any other suggestions?
Copying comment from the abandoned PR: @s-perron - when you come back from your vacation - do you have any thoughts on how to update the The tests has been added in #140120. |
…urce-array-subscript-one-elem-codegen
I have updated the test to use a struct with a constructor. These might not be allowed for much longer in Clang HLSL, but maybe by the time we restricts them there is going to be a different built-in type with a constructor that is not a resource that we can use instead. I could not figure out any other way to do this, I am open to suggestions! |
LGTM. Although someone with more familiarity should also take a look. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly seems good to me. A few comments.
@@ -4534,6 +4535,15 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E, | |||
LHS.getBaseInfo(), TBAAAccessInfo()); | |||
} | |||
|
|||
// The HLSL runtime handle the subscript expression on global resource arrays. | |||
if (getLangOpts().HLSL && (E->getType()->isHLSLResourceRecord() || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In what situation would we call in here where E->getType()->isHLSLResourceRecord()
is true but E->getType()->isHLSLResourceRecordArray()
is false?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
E->getType()
is result type of the ArraySubscriptExpr
. If the result of the indexing into a resource array is a single resource, E->getType()->isHLSLResourceRecord()
will be true and E->getType()->isHLSLResourceRecordArray()
will be false. If the result is a sub-array of a multi-dimensinal array, then it is going to be the other way around.
ExpParmTypes[ExplicitBinding ? 2 : 1] = AST.IntTy; | ||
|
||
CXXRecordDecl *ResDecl = ResTy->getAsCXXRecordDecl(); | ||
for (auto *Ctor : ResDecl->ctors()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish we had a good way to resolve this with overload resolution and have it captured in the AST... that said I'm not sure I have a good idea on how to do that. This does seem to do the correct thing.
…void alloc, use SmallVectorImpl, formatting
✅ With the latest revision this PR passed the C/C++ code formatter. |
Adds support for accessing individual resources from fixed-size global resource arrays.
Design proposal: https://github.com/llvm/wg-hlsl/blob/main/proposals/0028-resource-arrays.md
Enables indexing into globally scoped, fixed-size resource arrays to retrieve individual resources. The initialization logic is primarily handled during codegen. When a global resource array is indexed, the codegen translates the
ArraySubscriptExpr
AST node into a constructor call for the corresponding resource record type and binding.To support this behavior, Sema needs to ensure that:
Closes #145424